package com.dotflat.satistics.controller;

import com.dotflat.fin.PO.FinPlanOrderPO;
import com.dotflat.fin.PO.PayRecordPO;
import com.dotflat.fin.PO.WalletFlowPO;
import com.dotflat.fin.domain.FinPlanOrder;
import com.dotflat.fin.domain.PayRecord;
import com.dotflat.fin.domain.RefundRecord;
import com.dotflat.fin.domain.WalletFlow;
import com.dotflat.fin.service.FinPlanOrderService;
import com.dotflat.fin.service.PayRecordService;
import com.dotflat.fin.service.WalletFlowService;
import com.dotflat.refund.PO.RefundRecordPO;
import com.dotflat.refund.service.RefundRecordService;
import com.dotflat.satistics.dto.FinOverDTO;
import com.dotflat.satistics.dto.FinOverviewDTO;
import com.dotflat.satistics.dto.FinSatisticDTO;
import com.dotflat.satistics.dto.FinSatisticProductDTO;

import com.dotflat.satistics.repository.crm.ContractProductRepository;
import com.dotflat.satistics.repository.crm.IncomeRepository;
import com.dotflat.satistics.repository.crm.ProductRepository;
import com.dotflat.satistics.service.FinSatisticsService;
import com.matt.commons.dto.sso.StaffDTO;

import com.matt.commons.model.StatusResponse;

import com.matt.commons.page.PagerQuery;
import com.matt.domain.Clientele;
import com.matt.domain.Contract;
import com.matt.domain.Product;
import com.matt.ext.aliyun.OSSProcessService;
import com.matt.foundation.dto.teach.ChildDTO;
import com.matt.foundation.dto.teach.ChildrenNurseryRecordDTO;
import com.matt.foundation.dto.teach.ClassDTO;
import com.matt.foundation.dto.teach.EarlyChangeRecordDTO;
import com.matt.foundation.model.ErrorCode;
import com.matt.foundation.query.ContractQuery;
import com.matt.foundation.utils.DateUtil;
import com.matt.foundation.vo.ContractVo;
import com.matt.remoteCall.SsoCallService;
import com.matt.service.ClienteleService;
import com.matt.service.ContractService;
import com.matt.service.TeachCallService;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.matt.foundation.constant.CommonConstant.TICKET;

@Slf4j
@RestController
@RequestMapping("/fin/satistics")
@Api(tags = "财务 - 统计")
public class FinSatisticsController {

    @Autowired
    private ContractProductRepository contractProductRepository;

    @Autowired
    private IncomeRepository incomeRepository;

    @Autowired
    private FinPlanOrderService finPlanOrderService;

    @Autowired
    private PayRecordService payRecordService;

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private SsoCallService ssoCallService;

    @Autowired
    private TeachCallService teachCallService;

    @Autowired
    private FinSatisticsService finSatisticsService;

    @Autowired
    private RefundRecordService refundRecordService;

    @Autowired
    private OSSProcessService ossProcessService;

    @Autowired
    private ClienteleService clienteleService;

    @Autowired
    private ContractService contractService;

    @Value("${aliyun.oss.fileServerPrefix}")
    private String IMG_SERVICE_PREFIX;

    private FinOverviewDTO overview(String areaId, Date beginDate, Date endDate) {
        FinOverviewDTO result = new FinOverviewDTO();

        double income = incomeRepository.findIncomeByAreaAndTime(areaId, beginDate, endDate);
        result.setFullRecharge((long) income);

        result.setReturnAmount(0L);

        result.setTotal(result.getFullRecharge() - result.getReturnAmount());

        Long count = incomeRepository.findContratCount(areaId, beginDate, endDate);
        result.setContractCount(count);

        if (count > 0) {
            result.setUnitPrice((long) (income / count));
        } else {
            result.setUnitPrice(0L);
        }

        return result;
    }

    @RequestMapping(value = "/overview/money",  method = RequestMethod.POST)
    @ApiOperation(value = "根据时间查询收入支出", response = FinSatisticDTO.class)
    public StatusResponse overviewMoney(@RequestHeader(TICKET) String ticket,@RequestBody FinOverDTO dto) throws ParseException {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //开始时间
        Date startDateTime = dateFormat.parse(dto.getStartTime());
        //结束时间
        Date endDateTime = dateFormat.parse(dto.getEndTime());

        FinOverviewDTO result = overview(staff.getAreaId(), startDateTime, endDateTime);

        result.setCircle("时间查询数据");

        return StatusResponse.success(result);
    }

    @RequestMapping(value = "/overview/year", method = RequestMethod.POST)
    @ApiOperation(value = "年度概况", response = FinOverviewDTO.class)
    public StatusResponse overviewYear(@RequestHeader(TICKET) String ticket) {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        Date now = new Date();
        Date yearBegin = DateUtil.getBeginOfCurrentYear();

        FinOverviewDTO result = overview(staff.getAreaId(), yearBegin, now);

        result.setCircle("年度");

        return StatusResponse.success(result);
    }

    @RequestMapping(value = "/overview/month", method = RequestMethod.POST)
    @ApiOperation(value = "月度概况", response = FinSatisticDTO.class)
    public StatusResponse overviewMonth(@RequestHeader(TICKET) String ticket) {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        Date now = new Date();
        Date monthBegin = DateUtil.getBeginOfCurrentMonth();

        FinOverviewDTO result = overview(staff.getAreaId(), monthBegin, now);

        result.setCircle("月度");

        return StatusResponse.success(result);
    }

    @RequestMapping(value = "/overview/Quarter", method = RequestMethod.POST)
    @ApiOperation(value = "季度概况", response = FinSatisticDTO.class)
    public StatusResponse overviewQuarter(@RequestHeader(TICKET) String ticket) {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        Date now = new Date();
        Date monthBegin = DateUtil.getBeginOfCurrentQuarter();

        FinOverviewDTO result = overview(staff.getAreaId(), monthBegin, now);

        result.setCircle("季度");

        return StatusResponse.success(result);
    }

    @RequestMapping(value = "/income/curve/year", method = RequestMethod.GET)
    @ApiOperation(value = "年月收入曲线，可能需要匹配下x轴")
    public StatusResponse overviewCurveYear(@RequestHeader(TICKET) String ticket) {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        Date now = new Date();
        Date yearBegin = DateUtil.getBeginOfCurrentYear();

        List<Map<String, Object>> result = contractProductRepository.findUnitPriceByAreaId(staff.getAreaId(), yearBegin, now);

        return StatusResponse.success(result);
    }


    @RequestMapping(value = "/income/curve/month", method = RequestMethod.GET)
    @ApiOperation(value = "当月收入曲线，可能需要匹配下x轴")
    public StatusResponse overviewCurveMonth(@RequestHeader(TICKET) String ticket) {
        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        Date now = new Date();
        Date monthBegin = DateUtil.getBeginOfCurrentMonth();

        List<Map<String, Object>> result = contractProductRepository.findUnitPriceByAreaId(staff.getAreaId(), monthBegin, now);

        return StatusResponse.success(result);
    }

    @RequestMapping(value = "/clientele_pay_in_and_out", method = RequestMethod.POST)
    @ApiOperation(value = "单客户收退费统计", response = FinSatisticDTO.class)
    public StatusResponse clientelePayInAndOut(@RequestHeader(TICKET) String ticket,
                                               ContractQuery contractQuery) {
        if (StringUtils.isBlank(contractQuery.getClienteleId())){
            return StatusResponse.error(ErrorCode.CLIENTELE_EXIST, "指定客户不存在");
        }

        if (StringUtils.isBlank(contractQuery.getBeginDate()) || StringUtils.isBlank(contractQuery.getEndDate())){
            return StatusResponse.error(ErrorCode.PARAM_CAN_NOT_BLANK, "需要提供查询边界");
        }

        Date beginDate = DateUtil.parseToDate(contractQuery.getBeginDate());
        Date endDate = DateUtil.parseToDate(contractQuery.getEndDate());

        StaffDTO staff = ssoCallService.getStaffDTO(ticket);

        contractQuery.setAreaId(staff.getAreaId());

        //日期，
        //缴费项目 退费项目 消课类别 消课记录 合计
        Clientele client = clienteleService.getById(staff.getAreaId(), contractQuery.getClienteleId());
        if (client == null){
            return StatusResponse.error(ErrorCode.CLIENTELE_EXIST, "指定客户不存在");
        }

        //合同
        Page<ContractVo> contracts = contractService.findByCondition(contractQuery, null);

        //退款记录
        RefundRecordPO po = new RefundRecordPO();
        po.setClienteleId(client.getId());
        po.setCreatedL(beginDate);
        po.setCreatedR(endDate);
        List<RefundRecord> refundRecords = refundRecordService.list(staff.getAreaId(), staff.getId(), po);

        List<ChildrenNurseryRecordDTO> nurseryRecords = teachCallService.getNurseryRecords(
                client.getId(), contractQuery.getBeginDate(), contractQuery.getEndDate()
        );

        List<EarlyChangeRecordDTO> earlyRecords = teachCallService.getEarlyRecords(
                client.getId(), contractQuery.getBeginDate(), contractQuery.getEndDate()
        );

        Map ret = new HashMap<>();

        //按照日期来group
        Map<String, Map<String, List<Object>>> result = new TreeMap<>(); // 使用TreeMap按日期排序
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

        //合同
        for (ContractVo obj : contracts) {
            // 通过反射获取created字段
            String dateStr = dateFormat.format(obj.getCreated());

            // 初始化日期键
            result.putIfAbsent(dateStr, new HashMap<>());
            Map<String, List<Object>> dateMap = result.get(dateStr);

            // 初始化类型键
            dateMap.putIfAbsent("payRecords", new ArrayList<>());
            dateMap.get("payRecords").add(obj);
        }

        //退款记录
        for (RefundRecord obj : refundRecords) {
            // 通过反射获取created字段
            String dateStr = dateFormat.format(obj.getCreated());

            // 初始化日期键
            result.putIfAbsent(dateStr, new HashMap<>());
            Map<String, List<Object>> dateMap = result.get(dateStr);

            // 初始化类型键
            dateMap.putIfAbsent("refundRecords", new ArrayList<>());
            dateMap.get("refundRecords").add(obj);
        }

        //托班签到记录
        for (ChildrenNurseryRecordDTO obj : nurseryRecords) {
            // 通过反射获取created字段
            String dateStr = dateFormat.format(obj.getBeginDate());

            // 初始化日期键
            result.putIfAbsent(dateStr, new HashMap<>());
            Map<String, List<Object>> dateMap = result.get(dateStr);

            // 初始化类型键
            dateMap.putIfAbsent("nurseryRecords", new ArrayList<>());
            dateMap.get("nurseryRecords").add(obj);
        }

        //退款记录
        for (EarlyChangeRecordDTO obj : earlyRecords) {
            // 通过反射获取created字段
            String dateStr = dateFormat.format(obj.getCreated());

            // 初始化日期键
            result.putIfAbsent(dateStr, new HashMap<>());
            Map<String, List<Object>> dateMap = result.get(dateStr);

            // 初始化类型键
            dateMap.putIfAbsent("earlyRecords", new ArrayList<>());
            dateMap.get("earlyRecords").add(obj);
        }

        ret.put("client", client);

        ret.put("infos", result);
//        ret.put("refunds", refundRecords);
//        ret.put("nurseryRecords", nurseryRecords);
//        ret.put("earlyRecords", earlyRecords);

        return StatusResponse.success(ret);
    }

    @RequestMapping(value = "/pay_in_and_out", method = RequestMethod.POST)
    @ApiOperation(value = "收退费统计", response = FinSatisticDTO.class)
    public StatusResponse payInAndOut(@RequestHeader(TICKET) String ticket, String classId,
                                      String name, String term,
                                      String beginDate,String endDate , PagerQuery pagerQuery) {
        Page result = getPayInAndOutPage(ticket, classId, name, term, beginDate, endDate, pagerQuery);

        return StatusResponse.success(result);
    }

    private Page getPayInAndOutPage(@RequestHeader(TICKET) String ticket, String classId,
                                    String name, String term,
                                    String beginDate,String endDate , PagerQuery pagerQuery){
        String areaId = ssoCallService.getStaffDTO(ticket).getAreaId();
        if(beginDate != null && endDate != null){
            beginDate +=" 00:00:00";
            endDate +=" 23:59:59";
        }


        List<String> ids = null;
        List<ChildDTO> children = null;
        if (StringUtils.isNotBlank(classId) || StringUtils.isNotBlank(name)) {
            //有班级id的时候，限定客户id范围
            children = teachCallService.getChildClassList(ticket, 1, name, classId, null);
        }else {
            // 获取所有的班级宝宝
            pagerQuery = null; //让分页失效

            List<ClassDTO> classes = teachCallService.getClassList(ticket, 1);

            children = new ArrayList<>();

            for (ClassDTO c : classes){
                List<ChildDTO> cc = teachCallService.getChildClassList(ticket, 1, null, c.getId(), null);
                children.addAll(cc);
            }
        }

        //弄成ids
        if (children != null && children.size() > 0) {
            ids = children.stream().map(o -> o.getCrmClienteleId()).collect(Collectors.toList());
        }

        if (ids == null) {
            //限定空范围，直接返回空
            Page result = new PageImpl(new ArrayList());
            return result;
        }

        Page<FinSatisticDTO> result = finSatisticsService.generalFinSatistics(areaId, ids, name, term, beginDate, endDate, pagerQuery);

        if (result == null || result.getContent().size()  == 0 ) {
            PageRequest pageRequest =  PageRequest.of(0, 10);
            result= new PageImpl<>(new ArrayList<>(), pageRequest, 0);
            return result;
        }

        if (children == null) {
            //重新获取班级关系
            ids = result.getContent().stream().map(o -> o.getClienteleId()).collect(Collectors.toList());
            children = teachCallService.getChildClassList(ticket, 1, null, null, ids);
        }

        //重新装配班级信息
        if (children != null && children.size() > 0) {
            List<ChildDTO> finalChildren = children;
            result.getContent().stream().forEach(o -> {
                Optional<ChildDTO> op = finalChildren.stream().filter(c -> o.getClienteleId().equals(c.getCrmClienteleId())).findFirst();
                if (op.isPresent()) {
                    ChildDTO child = op.get();
                    o.setClassName(child.getClassNames());
                    o.setClassId(child.getClassIds());
                }
            });
        }

        //按照班级group
        if (StringUtils.isBlank(classId) && StringUtils.isBlank(name)) {
            List<FinSatisticDTO> grouped = groupAndSummarize(result.getContent());
            result = new PageImpl<>(grouped);
        }

        //给第一行装配表头
        List<Product> products = productRepository.findAllActiveProduct(areaId);

        for (int i = 0; i <result.getContent().size() ; i++) {
            FinSatisticDTO first = result.getContent().get(i);
            List<FinSatisticProductDTO> items = first.getItems();
            List<FinSatisticProductDTO> newItems = products.stream().map(product -> {
                for (FinSatisticProductDTO dto : items) {
                    if (product.getId().equals(dto.getProductId())) {
                        return dto;
                    }
                }

                FinSatisticProductDTO ret = new FinSatisticProductDTO();
//                ret.setAmount(0);
                ret.setCount(0);
                ret.setProductName(product.getName());
                ret.setProductId(product.getId());

                return ret;
            }).collect(Collectors.toList());
            first.setItems(newItems);
        }


        return result;
    }

    public List<FinSatisticDTO> groupAndSummarize(List<FinSatisticDTO> originalList) {
        // 1. 按 className 分组
        Map<String, List<FinSatisticDTO>> groupedByClassName = originalList.stream()
                .collect(Collectors.groupingBy(FinSatisticDTO::getClassName));

        // 2. 对每个分组的 FinSatisticDTO 进行进一步处理
        List<FinSatisticDTO> resultList = new ArrayList<>();

        for (Map.Entry<String, List<FinSatisticDTO>> entry : groupedByClassName.entrySet()) {
            String className = entry.getKey();
            List<FinSatisticDTO> group = entry.getValue();

            // 3. 合并同一个 className 下的所有 FinSatisticDTO 的 items
            Map<String, FinSatisticProductDTO> aggregatedItems = new HashMap<>();

            for (FinSatisticDTO dto : group) {
                for (FinSatisticProductDTO product : dto.getItems()) {
                    // 合并同一个 productName 的条目
                    aggregatedItems.merge(product.getProductName(), product, (existing, newItem) -> {
                        existing.setCount(existing.getCount() + newItem.getCount());
                        existing.setAmount(String.valueOf(Double.parseDouble(existing.getAmount()) + Double.parseDouble(newItem.getAmount())));
                        return existing;
                    });
                }
            }

            // 4. 将汇总后的 items 和 className 一起保存到新的 FinSatisticDTO 中
            FinSatisticDTO resultDTO = new FinSatisticDTO();
            resultDTO.setClassName(className);
            resultDTO.setItems(new ArrayList<>(aggregatedItems.values()));

            // 汇总 fullRecharge 和 returnAmount
            double fullRechargeTotal = group.stream().mapToDouble(dto -> Double.parseDouble(dto.getFullRecharge())).sum();
            double returnAmountTotal = group.stream().mapToDouble(dto -> Double.parseDouble(dto.getReturnAmount())).sum();
            resultDTO.setFullRecharge(String.valueOf(fullRechargeTotal));
            resultDTO.setReturnAmount(String.valueOf(returnAmountTotal));

            // 计算总计（收费 - 退费）
            double total = fullRechargeTotal - returnAmountTotal;
            resultDTO.setTotal(String.valueOf(total));

            resultList.add(resultDTO);
        }

        return resultList;
    }

    @RequestMapping(value = "/pay_in_and_out_export", method = RequestMethod.GET)
    @ApiOperation(value = "导出收退费统计excel")
    public StatusResponse exportPayInAndOut(@RequestHeader(TICKET) String ticket, String classId, String name, String term,String beginDate,String endDate , HttpServletResponse response) throws Exception {

        if (StringUtils.isNotBlank(beginDate)) {
            beginDate += " 00:00:00";
        }

        if (StringUtils.isNotBlank(endDate)) {
            endDate += " 23:59:59";
        }

        Page<FinSatisticDTO> result = getPayInAndOutPage(ticket, classId,name, term, beginDate, endDate, null);

        String[] headers = new String[6 + result.getContent().get(0).getItems().size()];
        headers[0] = "客户名称";

        headers[1] = "宝宝名称";

        headers[2] = "宝宝班级";

        headers[3] = "总收费";

        headers[4] = "总退费";

        headers[5] = "总计(收费-退费)";
        for (int i = 0; i < result.getContent().get(0).getItems().size(); i++) {
            headers[6 +  i] = result.getContent().get(0).getItems().get(i).getProductName();
        }
        String className = result.getContent().get(0).getClassName();

        List<Map<String, Object>> mapList = result.getContent().stream().map(o -> {
            Map<String, Object> map = new HashMap<>();
            map.put("clienteleName", o.getClienteleName());

            map.put("childName", o.getChildName());

            map.put("className", o.getClassName());

            map.put("fullRecharge", o.getFullRecharge());

            map.put("returnAmount", o.getReturnAmount());

            map.put("total", o.getTotal());
            for (int i = 0; i < o.getItems().size(); i++) {
                if ( o.getItems().get(i).getCount() > 0){
                    map.put(o.getItems().get(i).getProductName(), o.getItems().get(i).getAmount());
                }
            }
            return map;
        }).collect(Collectors.toList());

        // 声明一个工作薄
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 生成一个表格
        HSSFSheet sheet = workbook.createSheet("收退费统计报表");

        // 设置表格默认列宽度为15个字节
        sheet.setDefaultColumnWidth((short) 15);
        // 生成一个样式
        HSSFCellStyle style = workbook.createCellStyle();

        // 产生表格标题行
        HSSFRow row = sheet.createRow(0);

        for (short i = 0; i < headers.length; i++) {
            HSSFCell cell = row.createCell(i);
            cell.setCellStyle(style);
            HSSFRichTextString text = new HSSFRichTextString(headers[i]);
            cell.setCellValue(text);
        }

        for (int i = 0; i < mapList.size(); i++) {
            HSSFRow row1 = sheet.createRow(1 + i);

            HSSFCell cell1 = row1.createCell(0);
            cell1.setCellStyle(style);
            Object clienteleNameObj = mapList.get(i).get("clienteleName");
            cell1.setCellValue( clienteleNameObj != null ? clienteleNameObj.toString() : "");

            HSSFCell cell2 = row1.createCell(1);
            cell2.setCellStyle(style);
            Object childNameObj = mapList.get(i).get("childName");
            cell2.setCellValue(childNameObj != null ? childNameObj.toString() : "");

            HSSFCell cell3 = row1.createCell(2);
            cell3.setCellStyle(style);
            cell3.setCellValue(mapList.get(i).get("className").toString());

            HSSFCell cell4 = row1.createCell(3);
            cell4.setCellStyle(style);
            cell4.setCellValue(mapList.get(i).get("fullRecharge").toString());

            HSSFCell cell5 = row1.createCell(4);
            cell5.setCellStyle(style);
            cell5.setCellValue(mapList.get(i).get("returnAmount").toString());

            HSSFCell cell6 = row1.createCell(5);
            cell6.setCellStyle(style);
            cell6.setCellValue(mapList.get(i).get("total").toString());

            for (int j = 0; j < headers.length ; j++) {
                if (Objects.nonNull(mapList.get(i).get(headers[j]))){
                    if (StringUtils.equals( String.valueOf(mapList.get(i).get(headers[j])) ,headers[j])){
                        row1.createCell( j ).setCellValue(mapList.get(i).get(headers[j]).toString());
                    }
                }
            }
        }

        String tempPath = "";
        if (File.separatorChar == '/') {
            tempPath = "/tmp";
        } else {
            tempPath = "d:";
        }

        File outputFile = new File(tempPath + "/收退费统计报表.xlsx" );
        OutputStream out = new FileOutputStream(outputFile);
        workbook.write(out);
        Calendar cal = Calendar.getInstance();
        cal.setTime(DateUtil.parseToDateHour(beginDate));
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;

        String uploadPath = "teach/file/children_export_excel/" + DateTime.now().toString("yyyy-MM") + "/"+className+year+"年"+month+"月"+"收退费统计报表.xlsx" ;
        String relativePath = ossProcessService.aliyunOssUpload(uploadPath, outputFile);
        String uploadedResult = IMG_SERVICE_PREFIX + relativePath;

        outputFile.deleteOnExit();
        outputFile.delete();
        out.close();
        return StatusResponse.success(uploadedResult);
    }

}
